package com.hexiang.log;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
public class LogAspect {

    /**
     * 所有被{@link RequestMapping} 注解的类内的方法
     * 都会执行此切面。
     *
     * @param joinPoint 切入点
     * @return
     */
    @Around("@within(org.springframework.web.bind.annotation.RestController) ")
    Object requestMapping(ProceedingJoinPoint joinPoint) throws Throwable {
        return aroundImpl(joinPoint);
    }


    Object catchException(JoinPoint point, Throwable e) throws Throwable {
        //Api层异常捕获，返回系统错误码
        logger(point).error(String.format("%sError,request={}", point.getSignature().getName()), flatArgs(point.getArgs()), e);
        throw e;
    }


    Object aroundImpl(ProceedingJoinPoint joinPoint) throws Throwable {

        //没有启用，不需要处理
        if (!isEnabled(joinPoint)) {
            return joinPoint.proceed();
        }

        beforeExecute(joinPoint);
        Object resp = null;
        try {
            resp = joinPoint.proceed();
        } catch (Throwable throwable) {
            //处理异常
            catchException(joinPoint, throwable);
        }

        afterExecute(joinPoint, resp);

        return resp;
    }


    /**
     * 判断日志是否已经启动
     *
     * @param joinPoint
     * @return
     */
    boolean isEnabled(JoinPoint joinPoint) {

        Logging logging = resolveAnnotation(joinPoint);

        if (logging != null) {
            return logging.enable();
        }
        return true;
    }

    /**
     * 收集日志消息
     *
     * @param point
     * @return
     */
    void beforeExecute(JoinPoint point) {
        logger(point).info(String.format("%sStart,request={}", point.getSignature().getName()), flatArgs(point.getArgs()));
    }

    /**
     * 方法执行完成
     *
     * @param point    方法
     * @param response 响应
     */
    void afterExecute(JoinPoint point, Object response) {
        //调用之后打印日志
        logger(point).info(String.format("%sEnd,request={},response={}", point.getSignature().getName()), flatArgs(point.getArgs()), response);
    }


    /**
     * 解析日志注解
     *
     * @param joinPoint
     * @return
     */
    static Logging resolveAnnotation(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        Method method = resolveMethod(joinPoint);
        Logging logging = null;

        //默认从class上获取注解
        logging = AnnotationUtils.getAnnotation(target.getClass(), Logging.class);

        //使用方法的注解 ，方法优于class
        if (method.isAnnotationPresent(Logging.class)) {
            logging = AnnotationUtils.getAnnotation(method, Logging.class);
        }
        return logging;
    }


    /**
     * 获取调用的方法
     *
     * @param point
     * @return
     */
    static Method resolveMethod(JoinPoint point) {
        if (!(point.getSignature() instanceof MethodSignature)) {
            throw new IllegalStateException();
        }
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        return methodSignature.getMethod();
    }

    /**
     * 展开参数
     *
     * @param args
     * @return
     */
    static Object flatArgs(Object[] args) {
        if (args == null) {
            return null;
        }

        return Arrays.toString(args);
    }

    /**
     * 获取日志记录器
     *
     * @param joinPoint 切入点
     * @return
     */
    static Logger logger(JoinPoint joinPoint) {
        return LoggerFactory.getLogger(joinPoint.getTarget().getClass().getName());
    }
}